package agentes;

import java.util.ArrayList;
import modelo.Model;
import modelo.individuos.Vehicle;
import modelo.info.MapPoint;
import modelo.mapeado.Coordinate;
import modelo.simulacion.RouteGoogle;
import aglobe.container.agent.Agent;
import aglobe.ontology.AgentInfo;
import aglobe.ontology.Message;

public class AgenteConductor extends Agent {

	
	private AgentInfoParticipante info;
	private DriverMentalState mentalState;
	private Vehicle vehiculo;
	private Model modelo;
	//private Boolean overtaking;
	private ArrayList<AgentInfoOptionPlace> sameLane;
	private ArrayList<AgentInfoOptionPlace> forbiddenRoads;
	private ArrayList<AgentInfoOptionPlace> sameDirectionLanes;
	private ArrayList<AgentInfoOptionPlace> oppositeDirectionLanes;
	private ArrayList<AgentInfoOptionPlace> otherRoads;
	//private ArrayList<AgentInfoOptionPlace> places
		
	public void init(AgentInfo ai, int initState) {
		
		info = (AgentInfoParticipante)ai;
		System.out.println("Agent");
		System.out.println("Host: "+this.getAddress().getHost());
		System.out.println("Puerto: "+this.getAddress().getPort());
		System.out.println("Contenedor: "+this.getAddress().getContainerName());
		System.out.println("Nombre: "+this.getAddress().getName());
		System.out.println("AgentInfo");
		System.out.println("Clase principal: "+ai.getMainClass());
		System.out.println("Nombre: "+ai.getName());
		System.out.println("Nombre leible: "+ai.getReadableName());
		System.out.println("Tipo: "+ai.getType());
		System.out.println("Librerias: "+ai.getLibraries());
		System.out.println("Datos: "+ai.getData());
		System.out.println("");
	}

	public void handleIncomingMessage(Message arg0) {
	
	}
	
	public AgentInfoParticipante getInfo() {
		return info;
	}

	public void setInfo(AgentInfoParticipante info) {
		this.info = info;
	}
	
	public Vehicle getVehiculo() {
		return vehiculo;
	}
	
	public void setVehiculo(Vehicle vehiculo) {
		this.vehiculo = vehiculo;
	}

	public void setInitPlace(Model model,AgentInfoOptionPlace place) {
		
		modelo = model;
		//overtaking = false;
		initComponents(place);
		if (configuredRoute(place)) 
			mentalState.getMaker().encodeInstruction(mentalState.getMaker().
				getRuta().getCurrentInstruction());
		else
			System.out.println("Ruta no posible");
	}
	
	public void initComponents(AgentInfoOptionPlace place) {
		
		mentalState = new DriverMentalState(info,modelo);
		vehiculo = new Vehicle(modelo);
		sameLane = new ArrayList<AgentInfoOptionPlace>();
		forbiddenRoads = new ArrayList<AgentInfoOptionPlace>();
		sameDirectionLanes = new ArrayList<AgentInfoOptionPlace>();
		oppositeDirectionLanes = new ArrayList<AgentInfoOptionPlace>();
		otherRoads = new ArrayList<AgentInfoOptionPlace>();
		vehiculo.updatePosition(place.getX(),place.getY(),place.getPosicion());
		vehiculo.takePlace();
		vehiculo.addPoint(place);
	}
	
	public Boolean configuredRoute(AgentInfoOptionPlace place) {
		
		RouteGoogle route = null;
		Integer intento = 0;
		Boolean correcto = false;
		do {
			Coordinate inicio = modelo.getDriverMap()[place.getX()][place.getY()].getCoordinate();
			Coordinate fin = modelo.getSecond();
			Coordinate medio = getWayPoint(place,intento);
			route = new RouteGoogle(inicio.toString(),medio.toString(),fin.toString());
			if (mentalState.getMaker().isGoodRoute(place.getSentido(),route.getCurrentInstruction())) {
				correcto = true;
				mentalState.getMaker().setRuta(route);
				mentalState.getMaker().getRuta().pintarRecorrido();
			}
			else
				intento = intento+1;
		} while (!correcto && intento < 10);
		 
		return correcto;	
	}
	
	public Coordinate getWayPoint(AgentInfoOptionPlace place,Integer intento) {
		
		int x = place.getX();
		int y = place.getY();
		Coordinate resultado = null;
		int max = 0;
		int posx = -1;
		int posy = -1;
		int limit = intento*10;
		
		for (int i=x-limit;i<=x+limit;i++)
			for (int j=y-limit;j<=y+limit;j++) {
				if (modelo.dentroMapa(i,j) && !modelo.repeatPlace(x,y,i,j) &&
					modelo.getDriverMap()[i][j].getVia()) {
						int dx = Math.abs(x-i);
						int dy = Math.abs(y-j);
						int sum = dx+dy;
						if (max < sum) {
							posx = i;
							posy = j;
							max = sum;
						}
				}	
			}
		if (posx == -1 && posy == -1)
			resultado = modelo.getDriverMap()[place.getX()][place.getY()].getCoordinate();
		else
			resultado = modelo.getDriverMap()[posx][posy].getCoordinate();
		return resultado;
	}
	
	public void conducir() {
	
		sameLane.clear();
		sameDirectionLanes.clear();
		oppositeDirectionLanes.clear();
		forbiddenRoads.clear();
		otherRoads.clear();
		vehiculo.freePlace();
		getOptionPlaces();
		getNextPlace();
	}
	
	public void getOptionPlaces() { 
		
		int x = vehiculo.getX();
		int y = vehiculo.getY();
		int x1 = vehiculo.getLastPoints().get(0).getX();
		int y1 = vehiculo.getLastPoints().get(0).getY();
		for (int i=x-1;i<=x+1;i++) {
			for (int j=y-1;j<=y+1;j++) {
				int direction = getAgentDirection(x,y,i,j);
				if (!modelo.repeatPlace(x,y,i,j) && !modelo.repeatPlace(x1,y1,i,j))
					storePlace(modelo.getDriverMap(),direction,i,j);
			}
		}
	}
	
	public int getAgentDirection(int x,int y,int i,int j) {
		
		int resultado = -1;
		if (i > x && j > y)
			resultado = Model.Directions.AbDer.ordinal();
		else if (i > x && j < y)
			resultado = Model.Directions.ArrDer.ordinal();
		else if (i < x && j > y)
			resultado = Model.Directions.AbIzq.ordinal();
		else if (i < x && j < y)
			resultado = Model.Directions.ArrIzq.ordinal();
		else if (i == x && j > y)
			resultado = Model.Directions.Abajo.ordinal();
		else if (i == x && j < y)
			resultado = Model.Directions.Arriba.ordinal();
		else if (i > x && j == y)
			resultado = Model.Directions.Derecha.ordinal();
		else if (i < x && j == y)
			resultado = Model.Directions.Izquierda.ordinal();
		return resultado;
	}
	
	public void storePlace(MapPoint[][] mapa,int agentDirection,int x,int y) {
		
		if (modelo.dentroMapa(x,y) && mapa[x][y].getVia()) {
			MapPoint actual = mapa[vehiculo.getX()][vehiculo.getY()];
			int actualPlace = vehiculo.getPosition();
			
			for (int i=0;i<mapa[x][y].getDataList().size();i++) {
				int nextDirection = mapa[x][y].getDataList().get(i).getSentido();
				String nombre = mapa[x][y].getDataList().get(i).getNombre();
				String tipo = mapa[x][y].getDataList().get(i).getTipo();
				int carril = mapa[x][y].getDataList().get(i).getNumCarril();
				System.out.println(agentDirection+","+nextDirection+" "+x+","+y);
				if (modelo.forbiddenDirection(agentDirection,nextDirection)) {
					AgentInfoOptionPlace place = new AgentInfoOptionPlace(x,y,i,nombre,tipo,agentDirection);
					forbiddenRoads.add(place);
				}
				else if (actual.getDataList().get(actualPlace).getNombre().equals(nombre)) {
					if (!actual.getDataList().get(actualPlace).getNumCarril().equals(carril) &&
						modelo.opposedDirection(vehiculo.getRoadDirection(),nextDirection)) {
						AgentInfoOptionPlace place = new AgentInfoOptionPlace(x,y,i,nombre,tipo,agentDirection);
						oppositeDirectionLanes.add(place);
					}
					else if (!actual.getDataList().get(actualPlace).getNumCarril().equals(carril) &&
							!modelo.opposedDirection(vehiculo.getRoadDirection(),nextDirection)) {
						AgentInfoOptionPlace place = new AgentInfoOptionPlace(x,y,i,nombre,tipo,agentDirection);
						sameDirectionLanes.add(place);
					}
					else if (actual.getDataList().get(actualPlace).getNumCarril().equals(carril)) {
						AgentInfoOptionPlace place = new AgentInfoOptionPlace(x,y,i,nombre,tipo,agentDirection);
						sameLane.add(place);
					}	
				}
				else if (!actual.getDataList().get(actualPlace).getNombre().equals(nombre)) {
					AgentInfoOptionPlace place = new AgentInfoOptionPlace(x,y,i,nombre,tipo,agentDirection);
					otherRoads.add(place);
				}
			}
		}
	}
	
	public void getNextPlace() {

		if (!sameLane.isEmpty()) {
			if (sameLane.size() == 1) {
				AgentInfoOptionPlace place = sameLane.get(sameLane.size()-1);
				mentalState.getMaker().testInstruction(place);
				vehiculo.updatePosition(place.getX(),place.getY(),place.getPosicion());
				vehiculo.takePlace();
				vehiculo.addPoint(place);
			}
			else if (sameLane.size() == 2) {
				Boolean found = false;
				int posicion = 0;
				for (int i=0;i<sameLane.size()&&!found;i++) {
					if (mentalState.getMaker().isPlaceOnRoute(sameLane.get(i).getSentido())) {
							found = true;
							posicion = i;
					}
				}
				if (!found) {
					for (int i=0;i<sameLane.size()&&!found;i++) {
						int dx = Math.abs(vehiculo.getX()-sameLane.get(i).getX());
						int dy = Math.abs(vehiculo.getY()-sameLane.get(i).getY());
						if (dx == 0 || dy == 0) {
							posicion = i;
							found = true;
						}
					}
				}
					
				if (found) {
					AgentInfoOptionPlace place = sameLane.get(posicion);
					mentalState.getMaker().testInstruction(place);
					vehiculo.updatePosition(place.getX(),place.getY(),place.getPosicion());
					vehiculo.takePlace();
					vehiculo.addPoint(place);
				}
				else
					System.out.println("No encuentro place");
			}
			else if (sameLane.size() > 2) {
			
				System.out.println("OK "+sameLane.size());
				System.out.println(vehiculo.getX()+","+vehiculo.getY());
				Boolean found = false;
				int posicion = 0;
				int sentidoActual = vehiculo.getLastPoints().get(vehiculo.getLastPoints().size()-1).getSentido();
				for (int i=0;i<sameLane.size()&&!found;i++) {
					if (sentidoActual == sameLane.get(i).getSentido()) {
						posicion = i;
						found = true;
					}
				}
				if (!found) {
					for (int i=0;i<sameLane.size()&&!found;i++) {
						int dx = Math.abs(vehiculo.getX()-sameLane.get(i).getX());
						int dy = Math.abs(vehiculo.getY()-sameLane.get(i).getY());
						if (dx == 0 || dy == 0) {
							posicion = i;
							found = true;
						}	
					}
				}
				AgentInfoOptionPlace place = sameLane.get(posicion);
				vehiculo.updatePosition(place.getX(),place.getY(),place.getPosicion());
				vehiculo.takePlace();
				vehiculo.addPoint(place);
			}
		}
		else //if (!otherRoads.isEmpty()) {
			System.out.println("No roads");
		
	}
	
	public void finalize() {
		
		try {
			this.finalize();
			this.getContainer().getAgentManager().killAgent(getAddress().getName());
		} catch (Throwable e) {
			e.printStackTrace();
		}	
	}
}
